/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit;

import picounit.features.registry.RegistryEntries;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import junit.framework.Assert;

public abstract class Validator implements Registry {
	private final Operations operations = new Operations();
	private final RegistryEntries registryEntries = new RegistryEntries();
	private final Class<?>[] testClasses;

	private ExtendedTestResult extendedTestResult;

	public Validator(Class<?> ... testClasses) {
		this.testClasses = testClasses;
	}

	public void runSingle() {
		extendedTestResult = new ExtendedTestResult();

		registryEntries.register(Validator.class, this);

		PicoUnit picoUnit = new PicoUnit("FunctionalTest");
		registryEntries.registerWith(picoUnit);

		picoUnit.generateJUnitTest(rootClass(), new PassIfEqual(testClasses)).run(extendedTestResult);

		validate();
	}
	
	public Class<?> rootClass() {
		return testClasses[0];
	}
	
	public static class PassIfEqual implements TestFilter {
		private final Set<Class<?>> classes;

		public PassIfEqual(Class<?>[] classes) {
			this.classes = new HashSet<Class<?>>(Arrays.asList(classes));
		}

		public boolean matches(Class<?> testClass) {
			return classes.contains(testClass);
		}
	}

	public void record(String methodName) {
		operations.record(methodName);
	}

	public void record(String methodName, Object parameter) {
		operations.record(methodName, parameter);
	}

	protected void matches(String toMatch) {
		operations.matches(toMatch);
	}

	protected void has(String parameter) {
		Assert.assertNotNull(operations.get(parameter));
	}

	protected void assertNumberOfTestsEquals(int numTests) {
		Assert.assertEquals(numTests, extendedTestResult.runCount());
	}
	
	protected void assertNumberOfFailuresAndErrorsEquals(int numErrorsAndFailures) {
		Assert.assertEquals(numErrorsAndFailures,
			extendedTestResult.failureCount() + extendedTestResult.errorCount());
	}

	@SuppressWarnings("unchecked")
	protected void errorsInclude(String ... testNames) {
		for (String testName : testNames) {
			Assert.assertTrue("Errors do not include: " + testName,
				extendedTestResult.containsError(testName));
		}
	}

	@SuppressWarnings("unchecked")
	protected void failuresInclude(String ... testNames) {
		for (String testName : testNames) {
			Assert.assertTrue("Failures do not include: " + testName,
				extendedTestResult.containsFailure(testName));
		}
	}

	protected void successesInclude(String ... testNames) {
		for (String testName : testNames) {
			Assert.assertTrue("Successes do not include: " + testName,
				extendedTestResult.containsSuccess(testName));
		}
	}

	public void register(Class<?> implementation) {
		registryEntries.register(implementation);
	}

	public <B, D extends B> void register(Class<B> type, Class<D> implementation) {
		registryEntries.register(type, implementation);
	}

	public <B, D extends B> void register(Class<B> type, D instance) {
		registryEntries.register(type, instance);
	}

	public void register(String key, boolean value) {
		registryEntries.register(key, value);
	}

	public void register(String key, byte value) {
		registryEntries.register(key, value);
	}

	public void register(String key, char value) {
		registryEntries.register(key, value);
	}

	public void register(String key, double value) {
		registryEntries.register(key, value);
	}
	
	public void register(String key, float value) {
		registryEntries.register(key, value);
	}
	
	public void register(String key, int value) {
		registryEntries.register(key, value);
	}
	
	public void register(String key, long value) {
		registryEntries.register(key, value);
	}
	
	public void register(String key, short value) {
		registryEntries.register(key, value);
	}

	public void register(String key, String value) {
		registryEntries.register(key, value);
	}
	
	public <B> ImplementationCapturer<B> registerFactory(Class<B> type) {
		return new DelegatingImplementationCapturer<B>(type, this);
	}

	abstract public void validate();
}